package common

import (
	"encoding/base64"
	"encoding/json"
	"fmt"
	"net/http"
	"strings"
	"time"

	"github.com/go-playground/validator"
	"github.com/sirupsen/logrus"
	"github.com/xuelang-group/suanpan-appconnector/pkg/config"
	"github.com/xuelang-group/suanpan-appconnector/pkg/util"
)

const (
	SH_SOLVER_HELPER = "sh"
)

var inPanicHandler = false

type PanicHanler func(err error)
type ConvertableBoolean bool

func (b *ConvertableBoolean) UnmarshalJSON(data []byte) error {
	asString := string(data)
	if asString == "true" || asString == `"true"` {
		*b = true
	} else if asString == "false" || asString == `"false"` {
		*b = false
	} else {
		return fmt.Errorf("boolean unmarshal error: invalid input %s", asString)
	}
	return nil
}

type UploadList struct {
	IsDir     ConvertableBoolean `json:"isDir"`
	OssPath   string             `json:"ossPath"`
	LocalPath string             `json:"localPath"`
}

type DownloadList struct {
	IsDir     ConvertableBoolean `json:"isDir"`
	OssPath   string             `json:"ossPath"`
	LocalPath string             `json:"localPath"`
}

type WatchList struct {
	OssPath   string `json:"ossPath"`
	LocalPath string `json:"localPath"`
}

type GeneralParam struct {
	UserId       string         `json:"userId"`
	AppId        string         `json:"appId"`
	NodeId       string         `json:"nodeId"`
	RunType      string         `json:"runType"`
	Cmd          string         `json:"cmd"`
	DownloadList []DownloadList `json:"downloadList"`
	WatchList    []WatchList    `json:"watchList"`
	UploadList   []UploadList   `json:"uploadList"`
	ResultPath   string         `json:"resultPath"`
}

var generalParam *GeneralParam

var panicHandler PanicHanler

func init() {
	UpdateGeneralParam(&GeneralParam{})
}

func RegisterPanicFunction(handler PanicHanler) {
	panicHandler = handler
}

func GetApiServer() string {
	return GetMasterUrl()
}

func CheckArgsAvailable() error {
	args := config.GetArgs()
	if args == nil {
		return fmt.Errorf("args get failed please check ")

	}
	validate := validator.New()
	err := validate.Struct(args)
	if err != nil {
		return fmt.Errorf("validate args variables failed: %s", err.Error())
	}

	return nil
}

func UpdateGeneralParam(g *GeneralParam) {
	generalParam = g
}

func UpdateGeneralParamByBase64Str(inputStr string) error {
	strByte, err := base64.StdEncoding.DecodeString(inputStr)
	if err != nil {
		return (fmt.Errorf("error decoding b64 for param, %w", err))
	}
	logrus.Debugf("base 64 param decoded: %s", string(strByte))

	var g GeneralParam
	err = json.Unmarshal(strByte, &g)
	if err != nil {
		return (fmt.Errorf("error when unmashall json str from b64, %w", err))
	}
	UpdateGeneralParam(&g)
	return nil
}

func getAppCsName(appid string) string {
	if appid == "" {
		return ""
	}

	if strings.Contains(appid, ".") {
		s := strings.Split(appid, ".")
		return getAppCsName(s[0]) + "-" + s[1]
	}

	if strings.Contains(appid, "_") {
		return strings.Replace(("cron-" + appid), "_", "-", -1)
	}

	return "app-" + appid
}

func GetAppCsName() string {
	appid := GetAppId()
	return getAppCsName(appid)
}

func getUserNamespace(userId string) string {
	return strings.Replace("user-"+userId, "_", "-", -1)
}

func GetUserNamespace() string {
	return getUserNamespace(GetUserId())

}

func GetAppId() string {
	ret, err := util.GetIntOrString(generalParam.AppId)
	if err != nil {
		PanicError(fmt.Errorf("convert appid failed, data %v, err %s", generalParam.AppId, err.Error()))
	}
	return ret
}

func GetDownloadList() []DownloadList {
	return generalParam.DownloadList
}

func GetWatchList() []WatchList {
	return generalParam.WatchList
}

func GetUploadList() []UploadList {
	return generalParam.UploadList
}

func GetCmd() string {
	return generalParam.Cmd
}

func GetNodeId() string {
	return generalParam.NodeId
}

func GetUserId() string {
	return generalParam.UserId
}

func GetNetworkCacheKey(networkId string) string {
	return "nw-" + networkId
}

func PanicError(err error) {
	if err == nil {
		panic("err is nil why panic?")
	}
	if panicHandler == nil {
		logrus.Error("No panic handler panic directly")
	}
	if inPanicHandler {
		return
	}
	if panicHandler != nil {
		inPanicHandler = true
		go panicHandler(err)
	}
	fmt.Print("get error and 10s will panic, " + err.Error())
	//wait 10 second to logkit sent the info
	time.Sleep(10 * time.Second)
	panic(err.Error())
}

func GetShSolverServiceEndpoint() string {
	return config.GetEnv().ShSolverServiceEndpoint
}

// this is suanpan-web addr
// TODO:change this name
func GetMasterUrl() string {
	return fmt.Sprintf("http://suanpan-service.%s.svc.cluster.local:%s", config.GetArgs().App.Cs.MasterNamespace, config.GetEnv().SpPort)
	//TODO:desktop url
}

// this is suanpan master addr
func GetServiceMasterUrl() string {
	aName := GetAppCsName()
	uName := GetUserNamespace()
	port, err := util.GetIntOrString(config.GetArgs().App.Predict.Port)
	if err != nil {
		logrus.Error(err)
	}

	if aName == "" || uName == "" || port == "" {
		logrus.WithFields(logrus.Fields{
			"appCsName":     aName,
			"userNamespace": uName,
			"port":          port,
		}).Error("cannot build service master url")
		return ""
	}
	return fmt.Sprintf("http://%s.%s:%s", GetAppCsName(), GetUserNamespace(), port)
}

func GetOssLogRemotePath() string {
	if GetUserId() != "" && GetAppId() != "" && GetNodeId() != "" {
		return fmt.Sprintf("studio/%s/logs/%s/%s", GetUserId(), GetAppId(), GetNodeId())
	} else {
		return ""
	}
}

func GetHttpClient() *http.Client {
	return &http.Client{
		Timeout: 5 * time.Second,
	}
}

func PrintConnectGoVersion(ver, types, build string) string {
	return "ver:" + ver + "\n" + "Build time:" + build + "\n" + "Type:" + types + "\n"
}

func GetStreamRedisAddr(appId string) string {
	return fmt.Sprintf("app-%s-redis:6379", appId)
}

func GetEventsHubName(userId, appId string) string {
	return fmt.Sprintf("eventsHub-%s-%s", userId, appId)
}
